﻿#include "specwidget.h"
#include "ui_specwidget.h"
#include "qtgui/bookmarks.h"
#include <QSettings>
specWidget::specWidget(QWidget *parent) :
	QWidget(parent),
	ui(new Ui::specWidget)
{
	ui->setupUi(this);
	Bookmarks::create();
	m_pDtpMod = new QStandardItemModel(this);
	m_pDtpMod->appendRow(new QStandardItem("int8"));
	m_pDtpMod->appendRow(new QStandardItem("uint8"));
	m_pDtpMod->appendRow(new QStandardItem("int16 LSB"));
	m_pDtpMod->appendRow(new QStandardItem("int16 MSB"));

	ui->comboBox_dtp->setModel(m_pDtpMod);

	loadSettings();
	ui->plotter->setFftFill(true);
	ui->plotter->setPeakHold(true);
	//ui->plotter->setSampleRate(10000000);
	//ui->plotter->setSpanFreq(10000000);
	ui->plotter->setFftPlotColor(QColor(255,255,255));
	ui->plotter->setRunningState(true);
	ui->plotter->setFreqUnits(1000000);//MHz

	//选择带宽
	ui->plotter->setFilterBoxEnabled(true);
	//ui->plotter->setMaxBandiwdth(5000000);
	//ui->plotter->setHiLowCutFrequencies(-1000000,1000000);

	//中心频率
	//ui->plotter->setCenterFreq(101700000);
	//选择频率
	//ui->plotter->setDemodCenterFreq(102300000);
	ui->plotter->setTooltipsEnabled(true);
	ui->plotter->setPeakDetection(false,128.0);


	//连接
	connect (ui->plotter,&CPlotter::newDemodFreq,ui->freqCtrl,&CFreqCtrl::setFrequency);
	connect (ui->plotter,&CPlotter::newCenterFreq,this, &specWidget::evt_newCenterFreq);
	connect (ui->plotter,&CPlotter::newDemodFreq,this, &specWidget::evt_newDemodFreq);
	connect (ui->plotter,&CPlotter::newDemodFreq,this, &specWidget::slot_newDemodFreq);
	connect (ui->plotter,&CPlotter::newLowCutFreq,this, &specWidget::evt_newLowCutFreq);
	connect (ui->plotter,&CPlotter::newHighCutFreq,this, &specWidget::evt_newHighCutFreq);
	connect (ui->plotter,&CPlotter::newFilterFreq,this, &specWidget::evt_newFilterFreq);
	connect (ui->plotter,&CPlotter::pandapterRangeChanged,this, &specWidget::evt_pandapterRangeChanged);
	connect (ui->plotter,&CPlotter::newZoomLevel,this, &specWidget::evt_newZoomLevel);
	connect (ui->freqCtrl,&CFreqCtrl::newFrequency,this, &specWidget::evt_newCtrlFreq);

	m_pRevUdp = new cUdpReciever(this);
	connect(m_pRevUdp,&cUdpReciever::newSig,this,&specWidget::slot_new_wav,Qt::QueuedConnection);

	resetFFT();
}

specWidget::~specWidget()
{
	freeFFT();
	if (m_pRevUdp)
	{
		disconnect(m_pRevUdp,&cUdpReciever::newSig,this,&specWidget::slot_new_wav);
		m_pRevUdp->deleteLater();
		m_pRevUdp = nullptr;
	}
	delete ui;
}

void specWidget::slot_newDemodFreq(qint64 freq, qint64 delta)/* delta is the offset from the center */
{
	qint64 offfreq = freq - ui->plotter->getCenterFreq();
	if (ui->plotter->getSpanFreq()==0)
		return;
	double ratio = offfreq *1.0/ ui->plotter->getSpanFreq() + 0.5;

	int v = m_nFFTSize * ratio + .5;
	if (v<0) v = 0;

	if (v>=m_nFFTSize)
		v = m_nFFTSize - 1;
	if (!ui->checkBox_iq->isChecked())
	{
		v /= 2;
	}
	m_nCurrentCenter = v;

}

void specWidget::setNewFftData(const double *  rfftData, int size)
{
	ui->plotter->setNewFftData(rfftData,size);
}
void specWidget::setNewFftDataWf(const double *  rfftData, const double *  rwfData, int size)
{
	ui->plotter->setNewFftData(rfftData,rwfData,size);
}
void specWidget::setMaxPixmapSize(int w, int h)
{
	ui->plotter->setMaxPixmapSize(QSize(w,h));
}
void specWidget::setRunningState(bool running)
{
	ui->plotter->setRunningState(running);
}
void specWidget::setClickResolution(int clickres)
{
	ui->plotter->setClickResolution(clickres);
}
void specWidget::setFilterClickResolution(int clickres)
{
	ui->plotter->setFilterClickResolution(clickres);
}
void specWidget::setFilterBoxEnabled(bool enabled)
{
	ui->plotter->setFilterBoxEnabled(enabled);
}
void specWidget::setCenterLineEnabled(bool enabled)
{
	ui->plotter->setCenterLineEnabled(enabled);
}
void specWidget::setTooltipsEnabled(bool enabled){
	ui->plotter->setTooltipsEnabled(enabled);
}
void specWidget::setBookmarksEnabled(bool enabled){
	ui->plotter->setBookmarksEnabled(enabled);
}
void specWidget::setCenterFreq(quint64 f){
	ui->plotter->setCenterFreq(f);
}
void specWidget::setFreqUnits(qint32 unit){
	ui->plotter->setFreqUnits(unit);
}
void specWidget::setDemodCenterFreq(quint64 f){
	ui->plotter->setDemodCenterFreq(f);
}
void specWidget::setFilterOffset(qint64 freq_hz){
	ui->plotter->setFilterOffset(freq_hz);
}
qint64 specWidget::getFilterOffset(void){
	return ui->plotter->getFilterOffset();
}
int specWidget::getFilterBw(){
	return ui->plotter->getFilterBw();
}
void specWidget::setHiLowCutFrequencies(int LowCut, int HiCut){
	ui->plotter->setHiLowCutFrequencies(LowCut,HiCut);
}
void specWidget::getHiLowCutFrequencies(int *LowCut, int *HiCut){
	ui->plotter->getHiLowCutFrequencies(LowCut,HiCut);
}
void specWidget::setDemodRanges(int FLowCmin, int FLowCmax, int FHiCmin, int FHiCmax, bool symetric){
	ui->plotter->setDemodRanges(FLowCmin, FLowCmax, FHiCmin, FHiCmax, symetric);
}
void specWidget::setSpanFreq(quint32 s){
	ui->plotter->setSpanFreq(s);
}
void specWidget::setMaxBandiwdth(int m){
	ui->plotter->setMaxBandiwdth(m);
}
void specWidget::setHdivDelta(int delta){
	ui->plotter->setHdivDelta(delta);
}
void specWidget::setVdivDelta(int delta){
	ui->plotter->setVdivDelta(delta);
}
void specWidget::setFreqDigits(int digits){
	ui->plotter->setFreqDigits(digits);
}
void specWidget::setSampleRate(double rate){
	ui->plotter->setSampleRate(rate);
}
double specWidget::getSampleRate(void){
	return ui->plotter->getSampleRate();
}
void specWidget::setFftCenterFreq(qint64 f){
	ui->plotter->setFftCenterFreq(f);
}
int     specWidget::getNearestPeak(QPoint pt){
	return ui->plotter->getNearestPeak(pt);
}
void    specWidget::setWaterfallSpan(quint64 span_ms){
	ui->plotter->setWaterfallSpan(span_ms);
}
quint64 specWidget::getWfTimeRes(void){
	return ui->plotter->getWfTimeRes();
}
void    specWidget::setFftRate(int rate_hz){
	ui->plotter->setFftRate(rate_hz);
}
void    specWidget::clearWaterfall(void){
	return ui->plotter->clearWaterfall();
}
bool    specWidget::saveWaterfall(const QString & filename)
{
	return ui->plotter->saveWaterfall(filename);
}

void specWidget::on_pushButton_reset_clicked()
{
	saveSettings();
	loadSettings();
	resetFFT();
}

void specWidget::loadSettings()
{
	QSettings settings(QCoreApplication::applicationFilePath()+".ini", QSettings::IniFormat);

	const bool checkBox_iq = settings.value("spectrum/checkBox_iq",false).toBool();
	ui->checkBox_iq->setChecked(checkBox_iq);
	const double doubleSpinBox_spr = settings.value("spectrum/doubleSpinBox_spr",10).toDouble();
	ui->doubleSpinBox_spr->setValue(doubleSpinBox_spr);
	//Sample Rate
	ui->plotter->setSampleRate(doubleSpinBox_spr*1000000/(checkBox_iq?1:2));

	const double doubleSpinBox_center = settings.value("spectrum/doubleSpinBox_center",1200).toDouble();
	ui->doubleSpinBox_center->setValue(doubleSpinBox_center);
	//中心频率
	ui->plotter->setCenterFreq(doubleSpinBox_center * 1000000);
	//选择频率
	ui->plotter->setDemodCenterFreq(doubleSpinBox_center*1000000);
	ui->plotter->setHiLowCutFrequencies(-1000000,1000000);

	if (checkBox_iq)
	{
		ui->plotter->setSpanFreq(doubleSpinBox_spr*1000000);
		//选择带宽
		ui->plotter->setMaxBandiwdth(doubleSpinBox_spr*1000000);
	}
	else
	{
		ui->plotter->setSpanFreq(doubleSpinBox_spr*1000000/2);
		//选择带宽
		ui->plotter->setMaxBandiwdth(doubleSpinBox_spr*1000000/2);
	}

	const int spinBox_fftUDP = settings.value("spectrum/spinBox_fftUDP",20338).toInt();
	ui->spinBox_fftUDP->setValue(spinBox_fftUDP);

	const int spinBox_fftUpdate = settings.value("spectrum/spinBox_fftUpdate",20).toInt();
	ui->spinBox_fftUpdate->setValue(spinBox_fftUpdate);

	const int comboBox_dtp = settings.value("spectrum/comboBox_dtp",2).toInt();
	ui->comboBox_dtp->setCurrentIndex(comboBox_dtp);

	const int comboBox_fftSize = settings.value("spectrum/comboBox_fftSize",6).toInt();
	ui->comboBox_fftSize->setCurrentIndex(comboBox_fftSize);

	m_nFFTSize = (1<<comboBox_fftSize) * 128;
	if (m_nFFTSize>65536)
		m_nFFTSize = 65536;
	if (m_nFFTSize<128)
		m_nFFTSize = 128;

}
void specWidget::saveSettings()
{
	QSettings settings(QCoreApplication::applicationFilePath()+".ini", QSettings::IniFormat);
	const double doubleSpinBox_spr = ui->doubleSpinBox_spr->value();
	settings.setValue("spectrum/doubleSpinBox_spr",doubleSpinBox_spr);
	const double doubleSpinBox_center = ui->doubleSpinBox_center->value();
	settings.setValue("spectrum/doubleSpinBox_center",doubleSpinBox_center);
	const bool   checkBox_iq = ui->checkBox_iq->isChecked();
	settings.setValue("spectrum/checkBox_iq",checkBox_iq);
	const int spinBox_fftUDP = ui->spinBox_fftUDP->value();
	settings.setValue("spectrum/spinBox_fftUDP",spinBox_fftUDP);
	const int spinBox_fftUpdate = ui->spinBox_fftUpdate->value();
	settings.setValue("spectrum/spinBox_fftUpdate",spinBox_fftUpdate);
	const int comboBox_dtp = ui->comboBox_dtp->currentIndex();
	settings.setValue("spectrum/comboBox_dtp",comboBox_dtp);
	const int comboBox_fftSize = ui->comboBox_fftSize->currentIndex();
	settings.setValue("spectrum/comboBox_fftSize",comboBox_fftSize);
}
void specWidget::freeFFT()
{
	if (m_nTimerID>=0)
	{
		killTimer(m_nTimerID);
		m_nTimerID = -1;
	}
	if (m_pFFTPlan)
	{
		fftw_destroy_plan(m_pFFTPlan);
		m_pFFTPlan = nullptr;
	}
	if (m_pFFTIn)
	{
		fftw_free(m_pFFTIn);
		m_pFFTIn = nullptr;
	}
	if (m_pFFTOut)
	{
		fftw_free(m_pFFTOut);
		m_pFFTOut = nullptr;
	}
	m_dHammingWnd.clear();
	m_dFFTAmp.clear();
}
void specWidget::resetFFT()
{
	freeFFT();
	m_pFFTIn = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * m_nFFTSize);
	m_pFFTOut = (fftw_complex *) fftw_malloc(sizeof(fftw_complex) * m_nFFTSize);
	m_pFFTPlan = fftw_plan_dft_1d(m_nFFTSize,m_pFFTIn,m_pFFTOut,FFTW_FORWARD,FFTW_ESTIMATE);
	for (int i=0;i<m_nFFTSize;++i)
	{
		m_dHammingWnd.push_back(0.54 - 0.46 * cos (2*3.1415927 * i / (m_nFFTSize-1)));
		m_dFFTAmp.push_back(0);
	}
	m_pRevUdp->startListen(ui->spinBox_fftUDP->value());
	int updateIntelv = 1000.0 /ui->spinBox_fftUpdate->value() + .5;
	if (m_nTimerID>=0)
		killTimer(m_nTimerID);
	m_nTimerID = startTimer(updateIntelv);
}

void specWidget::slot_new_wav(QByteArray a)
{
	m_arrData.append(a);
	while (m_arrData.size()>= 1024 *1024 *16)
	{
		m_arrData.remove(0,1024*1024*15);
	}
}

void specWidget::appendWavReal(const double * pWav, const int count, double voltage_ref )
{
	if (!m_pFFTIn || !m_pFFTOut || !m_pFFTPlan || m_dHammingWnd.size()!=m_nFFTSize
			|| m_dFFTAmp.size()!=m_nFFTSize)
		return;
	memset(m_pFFTIn,0,sizeof(fftw_complex)*m_nFFTSize);
	const int nFFTCp = m_nFFTSize<count?m_nFFTSize:count;
	for (long long i=0;i<nFFTCp ;++i)
	{
		long long nHammingIdx = i * m_nFFTSize / nFFTCp;
		m_pFFTIn[i][0] = pWav[i] * m_dHammingWnd[nHammingIdx];
	}
	fftw_execute(m_pFFTPlan);

	const double minus = 10 *log(m_nFFTSize * voltage_ref * m_nFFTSize)/log(10);

	for (long i = 0; i< m_nFFTSize/2 ;++i)
	{
		m_dFFTAmp[i] = 5 * log (m_pFFTOut[i][0] * m_pFFTOut[i][0] + m_pFFTOut[i][1] * m_pFFTOut[i][1])/log(10) - minus + m_dManuAdd;
	}

	setNewFftData(m_dFFTAmp.data(),m_nFFTSize/2);
}
void specWidget::appendWavComplex(const double (* pWav)[2], const int count, double voltage_ref)
{
	if (!m_pFFTIn || !m_pFFTOut || !m_pFFTPlan || m_dHammingWnd.size()!=m_nFFTSize
			|| m_dFFTAmp.size()!=m_nFFTSize)
		return;
	memset(m_pFFTIn,0,sizeof(fftw_complex)*m_nFFTSize);
	const int nFFTCp = m_nFFTSize<count?m_nFFTSize:count;
	for (long long i=0;i<nFFTCp ;++i)
	{
		long long nHammingIdx = i * m_nFFTSize / nFFTCp;
		m_pFFTIn[i][0] = pWav[i][0] * m_dHammingWnd[nHammingIdx];
		m_pFFTIn[i][1] = pWav[i][1] * m_dHammingWnd[nHammingIdx];
	}
	fftw_execute(m_pFFTPlan);
	const double minus = 10 *log(m_nFFTSize * voltage_ref * m_nFFTSize)/log(10);

	for (long i = 0; i< m_nFFTSize ;++i)
	{
		m_dFFTAmp[(i+m_nFFTSize/2) % m_nFFTSize] = 5 * log (m_pFFTOut[i][0] * m_pFFTOut[i][0] + m_pFFTOut[i][1] * m_pFFTOut[i][1])/log(10) - minus + m_dManuAdd;
	}

	setNewFftData(m_dFFTAmp.data(),m_nFFTSize);
}


void specWidget::timerEvent(QTimerEvent * e)
{
	static std::shared_ptr<double> ppbuf(new double [65536*4]);
	static double * buf = ppbuf.get();
	static const double (*pBufIQ)[2] = (const double (*)[2])buf;
	const bool comp = ui->checkBox_iq->isChecked();
	const int dtp = ui->comboBox_dtp->currentIndex();
	static int cc_ct = 0;
	const int fft_bytes_needed = m_nFFTSize *(dtp/2+1) * (comp?2:1);
	//int fft
	//Change status light
	if (++cc_ct % 10 ==0)
	{
		static char notif[] = "/|\\-";
		static int  evct = 0;
		if (m_pRevUdp)
		{
			if (m_pRevUdp->isRunning())
					ui->label_fft_status->setText(tr("(Listening")+QString(notif[++evct%4])+")");
				else
					ui->label_fft_status->setText(tr("(Failed)"));
		}
	}
	if (e->timerId()==m_nTimerID && dtp>=0 && m_arrData.size()>=fft_bytes_needed)
	{
		const char * pInt8 = m_arrData.constData();
		const unsigned char * pUInt8 = (const unsigned char *) m_arrData.constData();
		const int sppoints = fft_bytes_needed/(dtp/2+1)/(comp?2:1);
		Q_ASSERT(sppoints == m_nFFTSize);
		double ref1v = 65536;
		switch (dtp)
		{
		case 0:
			if (comp==false)
			{
				for (int i=0;i<sppoints ;++i)
					buf[i] = pInt8[i];
			}
			else
			{
				for (int i=0;i<sppoints ;++i)
				{
					buf[i*2] = pInt8[i*2];
					buf[i*2+1] = pInt8[i*2+1];
				}
			}
			ref1v = 128;
			break;
		case 1:
			if (comp==false)
			{
				for (int i=0;i<sppoints ;++i)
					buf[i] = (int)(pUInt8[i]) - 127;
			}
			else
			{
				for (int i=0;i<sppoints ;++i)
				{
					buf[i*2] = (int)(pUInt8[i*2])-127;
					buf[i*2+1] = (int)(pUInt8[i*2+1])-127;
				}
			}
			ref1v = 128;
			break;
		case 2:
			if (comp==false)
			{
				for (int i=0;i<sppoints ;++i)
				{
					quint16 ui16_v = 0;
					ui16_v ^= (unsigned char)pInt8[i*2+1];
					ui16_v <<=8;
					ui16_v ^= (unsigned char)pInt8[i*2];
					qint16 * pi16 = (qint16 *) &ui16_v;
					buf[i] = *pi16;
				}
			}
			else
			{
				for (int i=0;i<sppoints ;++i)
				{
					{
						quint16 ui16_v = 0;
						ui16_v ^= (unsigned char)pInt8[i*4+1];
						ui16_v <<=8;
						ui16_v ^= (unsigned char)pInt8[i*4+0];
						qint16 * pi16 = (qint16 *) &ui16_v;
						buf[i*2] = *pi16;
					}
					{
						quint16 ui16_v = 0;
						ui16_v ^= (unsigned char)pInt8[i*4+3];
						ui16_v <<=8;
						ui16_v ^= (unsigned char)pInt8[i*4+2];
						qint16 * pi16 = (qint16 *) &ui16_v;
						buf[i*2+1] = *pi16;
					}
				}
			}
			ref1v = 16384;
			break;
		case 3:
			if (comp==false)
			{
				for (int i=0;i<sppoints ;++i)
				{
					quint16 ui16_v = 0;
					ui16_v ^= (unsigned char)pInt8[i*2];
					ui16_v <<=8;
					ui16_v ^= (unsigned char)pInt8[i*2+1];
					qint16 * pi16 = (qint16 *) &ui16_v;
					buf[i] = *pi16;
				}
			}
			else
			{
				for (int i=0;i<sppoints ;++i)
				{
					{
						quint16 ui16_v = 0;
						ui16_v ^= (unsigned char)pInt8[i*4];
						ui16_v <<=8;
						ui16_v ^= (unsigned char)pInt8[i*4+1];
						qint16 * pi16 = (qint16 *) &ui16_v;
						buf[i*2] = *pi16;
					}
					{
						quint16 ui16_v = 0;
						ui16_v ^= (unsigned char)pInt8[i*4+2];
						ui16_v <<=8;
						ui16_v ^= (unsigned char)pInt8[i*4+3];
						qint16 * pi16 = (qint16 *) &ui16_v;
						buf[i*2+1] = *pi16;
					}
				}
			}
			ref1v = 16384;
			break;
		default:
			if (comp==false)
			{
				for (int i=0;i<sppoints ;++i)
					buf[i] = pInt8[i];
			}
			else
			{
				for (int i=0;i<sppoints ;++i)
				{
					buf[i*2] = pInt8[i*2];
					buf[i*2+1] = pInt8[i*2+1];
				}
			}
			ref1v = 128;
			break;
		}
		if (comp==false)
		{
			appendWavReal(buf,sppoints,ref1v);
		}
		else
		{
			appendWavComplex(pBufIQ,sppoints,ref1v);
		}
		//Shink Array
		const int currDataSize = m_arrData.size();
		const int bytesLeft = currDataSize - fft_bytes_needed;
		const int ggroupsdel = bytesLeft / fft_bytes_needed;
		if (ggroupsdel)
			m_arrData.remove(0,ggroupsdel*fft_bytes_needed);
		Q_ASSERT(m_arrData.size()/fft_bytes_needed ==1);
		const int tail_del = (m_arrData.size()-fft_bytes_needed)/4 * 4;
		if (tail_del>0)
			m_arrData.remove(0,tail_del);

		if (m_nCurrentCenter>=0 && m_nCurrentCenter < m_nFFTSize)
		{
			ui->sMeter->setLevel(m_dFFTAmp[m_nCurrentCenter]);
			//ui->sMeter->setSqlLevel(m_dFFTAmp[m_nCurrentCenter]);
		}

	}
}



void specWidget::on_doubleSpinBox_center_2_valueChanged(double arg1)
{
	m_dManuAdd = arg1;
}

void specWidget::on_comboBox_fftSize_activated(int index)
{
	if (index)
		m_nFFTSize = 128 * (1<<index);
	if (m_nFFTSize>65536)
		m_nFFTSize = 65536;
	if (m_nFFTSize<128)
		m_nFFTSize = 128;
	saveSettings();
}
